import unittest
import os
import argparse
import yaml
import webbrowser
from http.server import HTTPServer, SimpleHTTPRequestHandler
import threading
import time
import sys
import re
from datetime import datetime

from utils.report_generator import generate_report
from utils.feishu_notifier import send_feishu_message


class CustomTestRunner(unittest.TextTestRunner):
    def run(self, test):
        test_results = []

        # 为每个测试用例单独计时
        for test_case in test:
            start_time = datetime.now()
            result = super().run(unittest.TestSuite([test_case]))
            end_time = datetime.now()

            execution_time = (end_time - start_time).total_seconds()

            # 收集测试结果
            case_name = test_case._testMethodName if hasattr(test_case, '_testMethodName') else str(test_case)

            # 获取测试用例相关的错误日志和截图
            error_details = self.get_test_case_errors(case_name)
            screenshots = self.get_test_case_screenshots(case_name)

            if result.failures:
                for failed_test, error in result.failures:
                    result_data = {
                        "test_case": case_name,
                        "status": "failed",
                        "execution_time": f"{execution_time:.2f}s",
                        "error_details": str(error) + "\n" + error_details,
                        "screenshots": screenshots
                    }
                    test_results.append(result_data)
            elif result.errors:
                for error_test, error in result.errors:
                    result_data = {
                        "test_case": case_name,
                        "status": "error",
                        "execution_time": f"{execution_time:.2f}s",
                        "error_details": str(error) + "\n" + error_details,
                        "screenshots": screenshots
                    }
                    test_results.append(result_data)
            else:
                test_results.append({
                    "test_case": case_name,
                    "status": "passed",
                    "execution_time": f"{execution_time:.2f}s",
                    "error_details": error_details,
                    "screenshots": screenshots
                })

        return test_results

    def get_test_case_errors(self, case_name):
        """获取测试用例相关的错误日志"""
        error_details = ""
        try:
            # 获取项目根目录的绝对路径
            project_root = os.path.dirname(os.path.abspath(__file__))
            today = datetime.now()
            log_dir = os.path.join(project_root, f"logs/{today.year}/{today.month:02d}")
            log_file = os.path.join(log_dir, f"{today.day:02d}-ERROR.log")
            
            if os.path.exists(log_file):
                # 读取日志文件
                with open(log_file, 'r', encoding='utf-8') as f:
                    lines = f.readlines()
                    
                # 查找与当前测试用例相关的错误信息
                for line in lines:
                    # 对于test_android用例，查找超时等待元素的错误
                    if "test_android" in case_name and "超时等待元素" in line:
                        # 提取时间戳和错误信息
                        match = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (?:AppAutomation - )?ERROR - (.*)', line)
                        if match:
                            timestamp = match.group(1)
                            error_msg = match.group(2)
                            error_details += f"\n日志时间: {timestamp}\n日志错误: {error_msg}\n"
                    # 也查找其他可能的错误
                    elif case_name in line:
                        match = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (?:AppAutomation - )?ERROR - (.*)', line)
                        if match:
                            timestamp = match.group(1)
                            error_msg = match.group(2)
                            error_details += f"\n日志时间: {timestamp}\n日志错误: {error_msg}\n"
                    # 添加更通用的匹配，只要日志中包含"ERROR"并且是今天的时间
                    elif "ERROR" in line:
                        # 检查是否是今天的时间
                        time_match = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
                        if time_match:
                            log_time_str = time_match.group(1)
                            log_time = datetime.strptime(log_time_str, '%Y-%m-%d %H:%M:%S')
                            # 如果是今天的日志
                            if log_time.date() == datetime.now().date():
                                error_match = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (?:AppAutomation - )?ERROR - (.*)', line)
                                if error_match:
                                    timestamp = error_match.group(1)
                                    error_msg = error_match.group(2)
                                    # 检查这条错误日志是否与当前测试用例相关
                                    # 通过检查时间戳是否接近来判断（在测试执行期间产生的日志）
                                    log_timestamp = datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S')
                                    # 如果时间在最近几分钟内，则认为是相关的
                                    if abs((log_timestamp - datetime.now()).total_seconds()) < 300:  # 5分钟内
                                        error_details += f"\n日志时间: {timestamp}\n日志错误: {error_msg}\n"
                    # 特别处理超时错误信息
                    elif "超时等待元素" in line:
                        # 检查是否是今天的时间
                        time_match = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
                        if time_match:
                            log_time_str = time_match.group(1)
                            log_time = datetime.strptime(log_time_str, '%Y-%m-%d %H:%M:%S')
                            # 如果是今天的日志
                            if log_time.date() == datetime.now().date():
                                error_match = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (?:AppAutomation - )?ERROR - (.*)', line)
                                if error_match:
                                    timestamp = error_match.group(1)
                                    error_msg = error_match.group(2)
                                    # 检查这条错误日志是否与当前测试用例相关
                                    # 通过检查时间戳是否接近来判断（在测试执行期间产生的日志）
                                    log_timestamp = datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S')
                                    # 如果时间在最近几分钟内，则认为是相关的
                                    if abs((log_timestamp - datetime.now()).total_seconds()) < 1800:  # 30分钟内
                                        error_details += f"\n日志时间: {timestamp}\n日志错误: {error_msg}\n"
        except Exception as e:
            # 如果读取日志文件出错，不处理
            pass
            
        return error_details

    def get_test_case_screenshots(self, case_name):
        """获取测试用例相关的截图文件列表"""
        screenshots = []
        try:
            # 获取项目根目录的绝对路径
            project_root = os.path.dirname(os.path.abspath(__file__))
            screenshot_dir = os.path.join(project_root, "reports", "screenshots")
            
            if os.path.exists(screenshot_dir):
                # 查找与当前测试用例相关的截图文件
                # 只匹配包含测试用例名称的截图文件
                all_screenshots = os.listdir(screenshot_dir)
                for file in all_screenshots:
                    if file.endswith(".png"):
                        # 检查文件名是否包含测试用例名称
                        if case_name in file:
                            screenshot_path = os.path.join(screenshot_dir, file)
                            screenshots.append(screenshot_path)
        except Exception as e:
            # 如果查找截图文件出错，不处理
            pass
            
        return screenshots

    def is_todays_screenshot(self, filename):
        """判断截图是否是今天的"""
        try:
            # 从文件名中提取日期，格式为 20251031
            # 查找文件名中的8位数字（日期）
            import re
            date_pattern = r'(\d{8})(?:_|\.)'
            matches = re.findall(date_pattern, filename)
            if matches:
                screenshot_date = matches[-1]  # 取最后一个匹配的日期
                today = datetime.now().strftime('%Y%m%d')
                return screenshot_date == today
        except Exception:
            pass
        return False


def flatten_suite(suite):
    """展平测试套件为用例列表"""
    tests = []
    for test in suite:
        if isinstance(test, unittest.TestSuite):
            tests.extend(flatten_suite(test))
        else:
            tests.append(test)
    return tests


class ReportServer:
    def __init__(self, port=8000):
        self.port = port
        self.server = None
        self.thread = None

    def start_server(self):
        """启动本地HTTP服务器"""
        # 切换到项目根目录
        project_root = os.path.dirname(os.path.abspath(__file__))
        os.chdir(project_root)
        
        handler = SimpleHTTPRequestHandler
        self.server = HTTPServer(("", self.port), handler)
        
        self.thread = threading.Thread(target=self.server.serve_forever)
        self.thread.daemon = True
        self.thread.start()
        
        print(f"本地服务器已启动，地址: http://localhost:{self.port}")

        # 等待服务器启动
        time.sleep(1)
        
        # 自动打开浏览器
        try:
            webbrowser.open(f"http://localhost:{self.port}/reports/report.html")
        except Exception as e:
            print(f"无法自动打开浏览器: {e}")

    def stop_server(self):
        """停止本地HTTP服务器"""
        if self.server:
            self.server.shutdown()
            self.server.server_close()
            print("本地服务器已停止")


if __name__ == "__main__":
    # ========== 配置区 ==========
    RUN_MODE = "single"  # 运行模式："all"-按顺序全量运行 / "single"-运行单个文件
    SINGLE_MODULE = "test_android"  # 单个运行时指定的模块名（不需要.py后缀）
    START_SERVER = True  # 是否启动本地服务器查看报告
    SERVER_PORT = 8000  # 服务器端口

    # 全量运行时模块执行顺序配置（数值越小越先执行）
    MODULE_ORDER = {
        # 'test_login': 0,
        # 'test_connect_devices': 1,
        'test_auto_case': 2,
        'test_android': 3,
        # 'test_DIY': 4,
        # 'test_Community_Operations': 5,
        # 'test_login_failure': 6,

    }
    # ===========================

    try:
        if RUN_MODE == "single":
            # 模式1：运行单个测试文件 --------------------------
            module_path = f"tests.{SINGLE_MODULE.strip()}"
            try:
                # 加载指定模块
                suite = unittest.defaultTestLoader.loadTestsFromName(module_path)
                print(f"正在运行单个测试模块: {SINGLE_MODULE}")
                # 展平测试套件以确保正确运行
                all_tests = flatten_suite(suite)
                suite = unittest.TestSuite(all_tests)
            except (AttributeError, ImportError):
                # 错误处理：列出可用模块
                available_modules = [f[:-3] for f in os.listdir('tests')
                                     if f.startswith('test') and f.endswith('.py')]
                print(f"\033[31m错误：找不到测试模块 '{SINGLE_MODULE}'\033[0m")
                print("可用的测试模块列表：")
                for mod in available_modules:
                    print(f"  - {mod}")
                sys.exit(1)
        else:
            # 模式2：全量按顺序运行 --------------------------
            # 自动发现所有测试模块
            suite = unittest.defaultTestLoader.discover(
                start_dir='tests',
                pattern='test*.py',
                top_level_dir=None
            )


            # 自定义排序函数
            def custom_sort(test_case):
                """按配置的模块顺序排序"""
                # 修改这里：从-2改为-3获取模块文件名
                module_name = test_case.id().split('.')[-3]  # 正确提取模块文件名（如test_login）
                # 只运行在MODULE_ORDER中定义的测试模块
                if module_name not in MODULE_ORDER:
                    return (9999, test_case.id())  # 将未定义的模块排在最后
                return (
                    MODULE_ORDER.get(module_name, 999),  # 优先按配置顺序
                    test_case.id()  # 次优按原始顺序
                )


            # 展平并排序测试用例
            all_tests = flatten_suite(suite)
            # 过滤掉不在MODULE_ORDER中的测试模块
            all_tests = [test for test in all_tests if test.id().split('.')[-3] in MODULE_ORDER]
            all_tests.sort(key=custom_sort)

            # 打印执行顺序
            print("测试模块执行顺序：")
            executed_modules = set()
            for test in all_tests:
                mod = test.id().split('.')[-2]
                if mod not in executed_modules:
                    print(f"  → {mod}")
                    executed_modules.add(mod)

            suite = unittest.TestSuite(all_tests)

        # 执行测试并生成报告
        runner = CustomTestRunner()
        test_results = runner.run(suite)
        report_path = generate_report(test_results)
        print(f"测试报告生成完毕，报告路径：{report_path}")

        # 读取配置文件判断是否发送飞书通知
        try:
            # 获取项目根目录的绝对路径
            project_root = os.path.dirname(os.path.abspath(__file__))
            config_path = os.path.join(project_root, "data", "config.yaml")
            
            with open(config_path, "r") as f:
                config = yaml.safe_load(f)

            feishu_config = config.get("feishu", {})
            enable_notification = feishu_config.get("enable_notification", False)

            if enable_notification:
                # 计算统计数据并发送飞书通知
                from utils.report_generator import calculate_statistics

                stats = calculate_statistics(test_results)

                send_feishu_message(
                    success_rate=stats['success_rate'],
                    total_cases=stats['total_cases'],
                    passed_cases=stats['passed_cases'],
                    failed_cases=stats['failed_cases'],  # 注意这里的对应关系
                    error_cases=stats['error_cases'],
                    skipped_cases=stats['skipped_cases'],
                    duration=stats['duration']
                )
            else:
                print("飞书通知已关闭，跳过发送通知。")
        except Exception as e:
            print(f"读取配置文件或发送通知时出现异常: {e}")
        
        # 启动本地服务器（如果需要），放在最后确保报告已生成
        server = None
        if START_SERVER:
            try:
                server = ReportServer(port=SERVER_PORT)
                server.start_server()
            except Exception as e:
                print(f"启动本地服务器失败: {e}")
                server = None

        # 如果启动了服务器，保持运行一段时间以便查看报告
        if server:
            print(f"\n服务器正在运行中，您可以通过以下地址查看报告:")
            print(f"http://localhost:{SERVER_PORT}/reports/report.html")
            print("按 Ctrl+C 停止服务器")
            try:
                while True:
                    time.sleep(1)
            except KeyboardInterrupt:
                server.stop_server()
                print("服务器已停止")
    except Exception as e:
        print(f"运行测试时出现异常: {e}")
        sys.exit(1)